﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.Windows;
using System.Windows.Threading;
using System.Windows.Controls;
using System.Windows.Media.Media3D;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Forms;
using System.Windows.Forms.Integration;
using SoundCapture;
using Sanford.Collections;
using Sanford.Multimedia;
using Sanford.Threading;
using Sanford.Multimedia.Midi;
using Sanford.Multimedia.Timers;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Media.Effects;
using System.Windows.Interop;

namespace GentlyWeep
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    /// 
    public delegate void GtEventHandler(object sender, WeepEventArgs e);

    public partial class MainWindow : Window
    {
        FrequencyInfoSource frequencyInfoSource;

        //New Item Midicon
        Midicon midicon = new Midicon();
        WeepEventArgs we = new WeepEventArgs();
        MidiPlayer midiplayer = new MidiPlayer();
        private bool playing = false;
        List<string> drumMidi = new List<string>();
        private Sequence sequence1 = new Sequence();
        private mySequencer sequencer1 = new mySequencer();
        DispatcherTimer timer1 = new DispatcherTimer();

       
        //end

        




        SoundCaptureDevice Listen_device = null;

        bool isListenning = false;
        static string[] NoteNames = { "A", "A#", "B/H", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#" };
        static int[] NoteMidi = { 69, 70, 71, 60, 61, 62, 63, 64, 65, 66, 67, 68 };
        double[] baseOctave = { 27.50, 29.14, 30.87, 16.35, 17.32, 18.35, 19.45, 20.60, 21.83, 23.12, 24.50, 25.96 };
        string[] noteOctave = { "E0", "F0", "G0", "G#0",
                                "A1", "A#1", "B/H1", "C1", "C#1", "D1", "D#1", "E1", "F1", "F#1", "G1", "G#1", 
                                "A2", "A#2", "B/H2", "C2", "C#2", "D2", "D#2", "E2", "F2", "F#2", "G2", "G#2",
                                "A3", "A#3", "B/H3", "C3", "C#3", "D3", "D#3", "E3", "F3", "F#3", "G3", "G#3",
                                "A4", "A#4", "B/H4", "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4",
                                "A5", "A#5", "B/H5", "C5", "C#5", "D5", "D#5", "E5", "F5", "F#5", "G5", "G#5",
                                "A6", "A#6", "B/H6", "C6", "C#6", "D6", "D#6", "E6", "F6", "F#6", "G6", "G#6",
                                "A7", "A#7", "B/H7", "C7", "C#7", "D7", "D#7", "E7", "F7", "F#7", "G7", "G#7",
                                "A8", "A#8", "B/H8", "C8"};
        static double ToneStep = Math.Pow(2, 1.0 / 12);
        int status = 0;
        int statusPlay = 0; 
        //int count_rt = 0;
        RecGuitarNote recNote = RecGuitarNote.GetInstance();
        string amp = "";
        bool tempoClick = false;
        bool bassVolClick = false;
        bool drumVolClick = false;
        bool sensitiveClick = false;
        Point startPOsition;
        System.Windows.Media.Color blueWeep = System.Windows.Media.Color.FromRgb(32, 161, 158);
        System.Windows.Media.Color lightblueWeep = System.Windows.Media.Color.FromRgb(148, 214, 212);//#FF94D6D4
        List<Grid> bassPlateName = new List<Grid>();
        int downRow=0;
        int downCol=0;

        #region startup
        public MainWindow()
        {
            this.InitializeComponent();
            this.InitializeSound();
            //scrollViewer.MouseDown += new MouseButtonEventHandler(scrollViewer_MouseDown);

        }

        void plate_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Border plate = (Border)sender;
          
        }

        void plate_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
        {
            Border plate = (Border)sender;
            DropShadowEffect dropShadowEffect = new DropShadowEffect();
            dropShadowEffect.ShadowDepth = 0;
            dropShadowEffect.Color = Colors.Black;
            dropShadowEffect.Opacity = 1;
            dropShadowEffect.BlurRadius = 12;

            plate.Effect = dropShadowEffect;

            String temp=plate.Name;
        }

        private void drawDrumPainoroll(int colNum)
        {

            for (int i = 1; i <= colNum; i++)
            {

                ColumnDefinition gridCol = new ColumnDefinition();
                gridCol.Width = new GridLength(20);
                gridDrum.ColumnDefinitions.Add(gridCol);
                SolidColorBrush color;
                //add rect
                for (int j = 0; j < 7; j++)
                {
                    Rectangle rect = new Rectangle();
                    color = new SolidColorBrush(Colors.DarkGray);
                    rect.Fill = color;
                    //rect.AllowDrop = true;
                    rect.MouseDown += new MouseButtonEventHandler(Grid_MouseDown);
                    //rect.MouseEnter += new System.Windows.Input.MouseEventHandler(Grid_MouseEnter);
                    rect.Focusable = true;
                    Grid.SetColumn(rect, i);
                    Grid.SetRow(rect, j);
                    gridDrum.Children.Add(rect);
                }
            }
        }

        private void drawBassPainoroll(int colNum, List<string> allnote ,Grid producer)
        {
            string noteOct;
            string status;
            
            
            for (int i = 1; i <= colNum; i++)
            {

                ColumnDefinition gridCol = new ColumnDefinition();
                
                if (producer.Name.Equals("gridBass"))
                {
                    gridCol.Width = new GridLength(20);
                    gridBass.ColumnDefinitions.Add(gridCol);
                }
                else
                {
                    gridCol.Width = new GridLength(7);
                    producer.ColumnDefinitions.Add(gridCol);
                }
                string[] temp = allnote[i - 1].Split('|');
                noteOct = temp[0] + temp[2];
                status = temp[5];
                SolidColorBrush color;
                int index = Array.IndexOf<string>(noteOctave, noteOct);
                //add rect
                for (int j = 0; j < 92; j++)
                {
                    if (!producer.Name.Equals("gridBass"))
                    {
                        RowDefinition gridRow = new RowDefinition();
                        gridRow.Height = new GridLength(2);
                        producer.RowDefinitions.Add(gridRow);
                    }
                    if (j == index)
                    {
                        Rectangle rect = new Rectangle();
                        if (status.Equals("n"))
                        {
                            color = new SolidColorBrush(lightblueWeep);
                        }
                        else
                        {
                            color = new SolidColorBrush(blueWeep);
                        }
                        rect.Fill = color;
                        //rect.AllowDrop = true;
                        rect.IsHitTestVisible = true;
                        
                        rect.Focusable = true;
                        Grid.SetColumn(rect, i);
                        Grid.SetRow(rect, j);
                        if (producer.Name.Equals("gridBass"))
                        {
                            rect.MouseDown += new MouseButtonEventHandler(Grid_MouseDown);
                            rect.MouseEnter += new System.Windows.Input.MouseEventHandler(Grid_MouseEnter);
                            rect.MouseLeftButtonUp+=new System.Windows.Input.MouseButtonEventHandler(rect_MouseLeftButtonUp);
                            gridBass.Children.Add(rect);
                        }
                        else
                        {
                            producer.Children.Add(rect);
                        }
                    }
                    else
                    {
                        Rectangle rect = new Rectangle();
                        color = new SolidColorBrush(Colors.DarkGray);
                        rect.Fill = color;
                        //rect.AllowDrop = true;
                        rect.MouseDown += new MouseButtonEventHandler(Grid_MouseDown);
                        rect.MouseEnter += new System.Windows.Input.MouseEventHandler(Grid_MouseEnter);
                        rect.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(rect_MouseLeftButtonUp);
                        rect.Focusable = true;
                        Grid.SetColumn(rect, i);
                        Grid.SetRow(rect, j);
                        if (producer.Name.Equals("gridBass"))
                        {
                            gridBass.Children.Add(rect);
                        }
                        else
                        {
                            producer.Children.Add(rect);
                        }
                    }
                }
            }

        }


        
        private void drawPainoroll(int colNum,List<string> allnote)
        {
            string noteOct;
            string status;
            for (int i = 1; i <= colNum; i++)
            {

                ColumnDefinition gridCol = new ColumnDefinition();
                gridCol.Width = new GridLength(20);
                gridGuitar.ColumnDefinitions.Add(gridCol);
                string[] temp = allnote[i - 1].Split('|');
                noteOct = temp[0] + temp[2];
                status = temp[5];
                SolidColorBrush color;
                int index = Array.IndexOf<string>(noteOctave, noteOct);
                //add rect
                for (int j = 0; j < 92; j++)
                {
                    if (j == index)
                    {
                        Rectangle rect = new Rectangle();
                        if (status.Equals("n"))
                        {
                            color = new SolidColorBrush(lightblueWeep);
                        }
                        else
                        {
                            color = new SolidColorBrush(blueWeep);
                        }
                        rect.Fill = color;
                        //rect.AllowDrop = true;
                        rect.IsHitTestVisible = true;
                        rect.MouseDown += new MouseButtonEventHandler(Grid_MouseDown);
                        rect.MouseEnter += new System.Windows.Input.MouseEventHandler(Grid_MouseEnter);
                        rect.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(rect_MouseLeftButtonUp);
                        rect.Focusable = true;
                        Grid.SetColumn(rect, i);
                        Grid.SetRow(rect, j);
                        gridGuitar.Children.Add(rect);
                    }
                    else
                    {
                        Rectangle rect = new Rectangle();
                        Rectangle test = new Rectangle();
                        color = new SolidColorBrush(Colors.DarkGray);
                        rect.Fill = color;
                        //rect.AllowDrop = true;
                        rect.MouseDown += new MouseButtonEventHandler(Grid_MouseDown);
                        rect.MouseEnter += new System.Windows.Input.MouseEventHandler(Grid_MouseEnter);
                        rect.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(rect_MouseLeftButtonUp);
                        rect.Focusable = true;
                        Grid.SetColumn(rect, i);
                        Grid.SetRow(rect, j);
                        gridGuitar.Children.Add(rect);
                    }
				}
			}
            
		}
        private void InitializeSound()
        {
            if (OutputDevice.DeviceCount == 0)
            {
                Console.WriteLine("No Device Detect");
                //Close();
            }
            else
            {
                this.sequencer1.Position = 0;
                this.sequencer1.Sequence = this.sequence1;
                this.sequencer1.PlayingCompleted += new System.EventHandler(this.HandlePlayingCompleted);
                this.sequencer1.ChannelMessagePlayed += new System.EventHandler<Sanford.Multimedia.Midi.ChannelMessageEventArgs>(this.HandleChannelMessagePlayed);
                this.sequencer1.Stopped += new System.EventHandler<Sanford.Multimedia.Midi.StoppedEventArgs>(this.HandleStopped);
                this.midiplayer.midiOn += new GtEventHandler (this.HandleMidiOn);
                sequence1.LoadAsync("Midi//Onstart.mid");


                //Track tr = new Track();
                //tr.Insert(0, new MetaMessage(MetaType.InstrumentName, new byte[] { 0x6C, 0x65, 0x6E, 0x6E, 0x61, 0x68, 0x43 }));
                //tr.Insert(45, new ChannelMessage(ChannelCommand.NoteOn,6,61,127));
                //tr.Insert(50, new ChannelMessage(ChannelCommand.NoteOff, 6, 61, 127));
                //tr.Insert(55, new ChannelMessage(ChannelCommand.NoteOn, 6, 61, 127));
                //tr.Insert(60, new ChannelMessage(ChannelCommand.NoteOff, 6, 61, 127));
                //tr.Insert(70, new MetaMessage(MetaType.EndOfTrack, new byte[] {}));
                //this.sequencer1.Sequence = this.sequence1;
                //sequence1.Add(tr);
                this.sequencer1.SysExMessagePlayed += new System.EventHandler<Sanford.Multimedia.Midi.SysExMessageEventArgs>(this.HandleSysExMessagePlayed);
                this.sequencer1.Chased += new System.EventHandler<Sanford.Multimedia.Midi.ChasedEventArgs>(this.HandleChased);
                sequence1.LoadAsync("C://Users//Korn//Desktop//untitled.mid");
                

                this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
                
            }
        }

        private void DragWindow(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            
            //this.DragMove();
        }

        private void closeBT_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            this.Close();
        }

        private void Grid_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            
            Rectangle r = (Rectangle)sender;
            int row = Grid.GetRow(r);
            int col = Grid.GetColumn(r);
            SolidColorBrush myBrush = new SolidColorBrush(Colors.DarkGray);
            //midi_con.midiSound(row + 60,6);
            Brush curColor = r.Fill;
            Console.WriteLine(curColor);
            
            if(curColor.ToString().Equals("#FFA9A9A9")){
                //Console.WriteLine("เทา");
                myBrush = new SolidColorBrush(blueWeep);

                for (int i = col*92; i <= col*92+91; i++)
                {
                    if (i != gridParent.Children.IndexOf(r))
                    {
                        Rectangle rect2 = (Rectangle)gridParent.Children[i];
                        rect2.Fill = new SolidColorBrush(Colors.DarkGray);
                    }
                }
            }
            else if (curColor.ToString().Equals("#FF20A19E"))
            {
                //Console.WriteLine("น้ำเงินเข็ม");
                myBrush = new SolidColorBrush(Colors.DarkGray);
            }
            //Console.WriteLine(e.ButtonState);
			r.Fill = myBrush;
            
		}
        private void Grid_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
        {
            //Console.WriteLine(e.LeftButton);
            Rectangle r = (Rectangle)sender;
            int row = Grid.GetRow(r);
            int col = Grid.GetColumn(r);
            Grid gridParent = (Grid)r.Parent;
            SolidColorBrush myBrush = new SolidColorBrush(lightblueWeep);
            
            if (e.LeftButton.ToString().Equals("Pressed") && checkDragRoll(row,col))
            {
                r.Fill = myBrush;
                for (int i = col * 92; i <= col * 92 + 91; i++)
                {
                    if (i != gridParent.Children.IndexOf(r))
                    {
                        Rectangle rect2 = (Rectangle)gridParent.Children[i];
                        rect2.Fill = new SolidColorBrush(Colors.DarkGray);
                    }
                }
            }
        }
        private void rect_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            Rectangle r = (Rectangle)sender;
            Grid gridParent = (Grid)r.Parent;
            int allItem = gridParent.Children.Count;
            if (downCol != 1)
            {
                for (int i = downCol * 92; i <= allItem - 1; i++)
                {
                    Rectangle rect2 = (Rectangle)gridParent.Children[i];
                    Rectangle beforeRect2 = (Rectangle)gridParent.Children[i - 92];
                    if (i != gridParent.Children.IndexOf(r) && rect2.Fill.ToString().Equals("#FF94D6D4") && beforeRect2.Fill.ToString().Equals("#FFA9A9A9"))
                    {
                        rect2.Fill = new SolidColorBrush(blueWeep);
                    }
                }
            }
        }


        private void readPianoroll(Grid gridParent)
        {
            
            int allItem = gridParent.Children.Count;
            for (int i = 92; i <= allItem - 1; i++)
            {
                Rectangle rect = (Rectangle)gridParent.Children[i];
                if (!rect.Fill.ToString().Equals("#FFA9A9A9"))
                {
                    Console.WriteLine(rect.Fill);
                }
            }
        }


        private bool checkDragRoll(int enterRow, int enterCol)
        {
            if (enterRow == downRow && enterCol > downCol)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        
        
        #endregion startup

        #region SoundDetected
        public bool IsListenning
        {
            get { return isListenning; }
        }


        private void StartListenning(SoundCaptureDevice device)
        {
            isListenning = true;
            frequencyInfoSource = new SoundFrequencyInfoSource(device);
            frequencyInfoSource.FrequencyDetected += new EventHandler<FrequencyDetectedEventArgs>(frequencyInfoSource_FrequencyDetected);
            frequencyInfoSource.Listen();
        }

        void frequencyInfoSource_FrequencyDetected(object sender, FrequencyDetectedEventArgs e)
        {
            if (Dispatcher.Thread == Thread.CurrentThread) // เชคว่า Thread นี้อยูาใน Background thread หรือไม่ 
            {
                double frequency = e.Frequency;
                double closestFrequency = 0;
                int noteMidi = 0;
                string noteName = "";
                double octave = 0;
                if (frequency > 0)
                {
                    FindClosestNote(frequency, out closestFrequency, out noteName, out noteMidi, out octave);
                }
                NoteText.Text = noteName;
                //Console.WriteLine(noteName);
                amp = recNote.getAmp();
                recNote.recNote(noteMidi, amp, octave, closestFrequency, noteName);
                //Console.WriteLine("octave : " + octave);
            }
            else
            {
                Dispatcher.BeginInvoke(new EventHandler<FrequencyDetectedEventArgs>(frequencyInfoSource_FrequencyDetected), sender, e); // ทำการเลื่อนจาก Background thread มาเป็น Main Thread เพื่อให้ UI ทำงานได้เตมที่ เพราะ Background Thread จะเรียกใช้ control ใน main thread ไม่ได้ เพื่อป้องกันการเกิด cross-thread exception
            }
        }

        private void FindClosestNote(double frequency, out double closestFrequency, out string noteName, out int noteMidi, out double octave)
        {
            const double AFrequency = 440.0;
            const int ToneIndexOffsetToPositives = 120;
            int toneIndex = (int)Math.Round(Math.Log(frequency / AFrequency, ToneStep));
            noteName = NoteNames[(ToneIndexOffsetToPositives + toneIndex) % NoteNames.Length];
            noteMidi = NoteMidi[(ToneIndexOffsetToPositives + toneIndex) % NoteMidi.Length];
            octave = baseOctave[(ToneIndexOffsetToPositives + toneIndex) % baseOctave.Length];
            closestFrequency = Math.Pow(ToneStep, toneIndex) * AFrequency;
        }


        private void stop_bt_Click(object sender, EventArgs e)
        {
            StopListenning();
        }

        private void StopListenning()
        {
            isListenning = false;
            frequencyInfoSource.Stop();
            frequencyInfoSource.FrequencyDetected -= new EventHandler<FrequencyDetectedEventArgs>(frequencyInfoSource_FrequencyDetected);
            frequencyInfoSource = null;
        }

        private void ShowDevice(object sender, System.Windows.RoutedEventArgs e)
        {
            CaptureDevice form = new CaptureDevice();
            SoundCaptureDevice device = null;

            if (form.ShowDialog() == true)
            {
                device = form.SelectedDevice;
            }

            if (device != null)
            {
                DeviceText.Text = device.Name;
                Listen_device = device;

            }
        }

        private void Record_bt_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.Controls.Button bt = (System.Windows.Controls.Button)sender;
            List<string> allnote = new List<string>();
            if (status == 0)
            {
                bt.Content = "Stop Record";
                StartListenning(Listen_device);
                status = 1;
            }
            else if (status == 1)
            {
                isListenning = false;
                frequencyInfoSource.Stop();
                frequencyInfoSource.FrequencyDetected -= new EventHandler<FrequencyDetectedEventArgs>(frequencyInfoSource_FrequencyDetected);
                frequencyInfoSource = null;
                status = 0;
                bt.Content = "Record";
                allnote = recNote.getNote();
                Console.WriteLine(allnote.Count);
                drawPainoroll(allnote.Count,allnote);
                for (int i = 0; i < allnote.Count; i++)
                {
                    Console.WriteLine(i + "            " + allnote[i]);
                }

            }

        }
        #endregion SoundDetected


        private void play_bt_Click(object sender, RoutedEventArgs e)
        {
            this.start();
        }

        public void start()
        {
            try
            {
                playing = true;
                sequencer1.Start();
                timer1.Start();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error" + ex);
            }
        }

        public void stop()
        {
            try
            {
                playing = false;
                sequencer1.Stop();
                timer1.Stop();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error" + ex);
            }
        }

        public void continue_(object sender, EventArgs e)
        {
            try
            {
                playing = true;
                sequencer1.Continue();
                timer1.Start();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error" + ex);
            }
        }

        private void HandleChannelMessagePlayed(object sender, ChannelMessageEventArgs e)
        {
            midicon.out_send(e);
        }

        private void HandleMidiOn(object sender, WeepEventArgs e)
        {
            //midicon.out_send2(e);
        }
        //this.Dispatcher.Invoke(
        //  System.Windows.Threading.DispatcherPriority.Normal,
        //    new Action(
        //        delegate()
        //        {
        //            drumControl1.Send(e.Message);
        //        }
        //    ));

        private void HandleStopped(object sender, StoppedEventArgs e)
        {
            foreach (ChannelMessage message in e.Messages)
            {
                //outDevice.Send(message);
            }
            
        }

        private void HandlePlayingCompleted(object sender, EventArgs e)
        {
            //sequencer1.Continue();
            timer1.Stop();

        }

        private void genreCB_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            midicon.Genre = genreCB.Text;
            drumMidi = midicon.selectDrum();
            drumMidi = midicon.selectBass();
        }

        private void keyCB_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            switch (keyCB.Text)
            {
                case (string)"C":
                    midicon.KeyUp = 0;
                    break;
                case (string)"D":
                    midicon.KeyUp = 1;
                    break;
                case (string)"E":
                    midicon.KeyUp = 2;
                    break;
                case (string)"F":
                    midicon.KeyUp = 3;
                    break;
                case (string)"G":
                    midicon.KeyUp = 4;
                    break;
                case (string)"A":
                    midicon.KeyUp = 5;
                    break;
                case (string)"B":
                    midicon.KeyUp = 6;
                    break;
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            //drumMidi = midicon.selectDrum(1,1,"genre",1);
            //drumMidi = midicon.selectBass(1, 1, "genre", 1);
            //if (sequencer1.Position >= sequence1.GetLength() - 2)
            //{
            //    // sequence1.LoadAsync("Midi//"+D1+".mid");
            //    stop();
            //    this.sequencer1.Sequence = this.sequence1;
            //    this.sequencer1.Position = 0;
            //    start();
            //}
        }
    }
}
